{T}

Server-Sent Events (SSE)

Server-Sent Events(SSE)是一种基于 HTTP 的服务器向客户端单向推送技术,使用 EventSource 接口接收服务端持续发送的文本事件流。它适合股票行情、消息通知、日志推送等"服务器主动推、客户端只收"的场景。

与 WebSocket 的区别

维度SSEWebSocket
通信方向服务器 → 客户端(单向)双向全双工
传输协议基于 HTTP/HTTPS独立 ws:// 协议
数据格式纯文本事件流二进制 / 文本帧
自动重连浏览器原生支持需手动实现
断点续传支持 Last-Event-ID需自行设计
复杂度中高
提示

若仅需服务器推送、不需要客户端频繁回传,优先选择 SSE,实现成本远低于 WebSocket。

客户端用法

js
const source = new EventSource('https://api.example.com/stream');
 
source.onmessage = (event) => {
  console.log('收到消息:', event.data);
};
 
source.onerror = (error) => {
  console.error('连接异常:', error);
};

自定义事件类型

服务端可通过 event: 字段指定事件名,客户端用 addEventListener 监听:

js
source.addEventListener('price', (event) => {
  const data = JSON.parse(event.data);
  console.log('股价更新:', data.symbol, data.price);
});
 
source.addEventListener('alert', (event) => {
  console.warn('系统告警:', event.data);
});

关闭连接

js
source.close();

服务端实现要求

SSE 要求服务端响应满足以下条件(以 Node.js 为例):

js
// Node.js (Express) 示例
app.get('/stream', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
 
  const interval = setInterval(() => {
    const payload = JSON.stringify({ time: new Date().toISOString() });
    res.write(`data: ${payload}\n\n`);
  }, 1000);
 
  req.on('close', () => {
    clearInterval(interval);
    res.end();
  });
});

事件流格式规范

每条消息由若干字段组成,以空行 \n\n 结束:

plaintext
event: price
data: {"symbol": "AAPL", "price": 192.3}
id: 1024
 
data: 这是一条默认事件(onmessage 接收)
字段作用
data:消息内容(可多行,拼接为单行)
event:自定义事件类型
id:事件 ID,断线后由 Last-Event-ID 头回传
retry:重连间隔时间(毫秒)

断线重连与续传

浏览器在连接意外断开时会自动重连,并在请求头带上最后一次收到的 Last-Event-ID

js
source.onopen = () => console.log('连接已建立');

服务端可据此从断点继续推送,避免消息丢失:

js
app.get('/stream', (req, res) => {
  const lastId = Number(req.headers['last-event-id'] || 0);
  // 从 lastId 之后的消息继续推送……
});

完整可运行示例

以下示例用纯前端模拟 EventSource 的推送与自动重连语义(无需后端即可观察效果):

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>SSE 推送演示</title>
</head>
<body>
  <button id="startBtn">开始推送</button>
  <button id="stopBtn">停止</button>
  <pre id="log"></pre>
 
  <script>
    const log = document.getElementById('log');
    let timer = null, count = 0;
 
    document.getElementById('startBtn').onclick = () => {
      if (timer) return;
      log.textContent = '连接已建立(模拟 EventSource)\n';
      timer = setInterval(() => {
        count++;
        const time = new Date().toLocaleTimeString();
        log.textContent += `📨 #${count}  ${time}  服务器推送数据\n`;
        if (count % 5 === 0) {
          log.textContent += `↻ 自动重连机制就绪(心跳 #${count})\n`;
        }
      }, 1000);
    };
 
    document.getElementById('stopBtn').onclick = () => {
      clearInterval(timer);
      timer = null;
      log.textContent += '连接已关闭 (source.close())\n';
    };
  </script>
</body>
</html>

浏览器兼容性

所有现代浏览器均支持 EventSource。注意旧版 IE/Edge(非 Chromium 内核)不支持,生产环境需评估兼容方案(如 polyfill 或降级为轮询)。